home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
C/C++ Users Group Library 1996 July
/
C-C++ Users Group Library July 1996.iso
/
vol_300
/
367_01
/
futi14as.zoo
/
mvdir.c
< prev
next >
Wrap
C/C++ Source or Header
|
1990-09-04
|
6KB
|
245 lines
/* mvdir -- rename directory
Copyright (C) 1990 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 1, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
/* Helper program for GNU mv on machines that lack the rename system call.
Usage: mvdir from to
FROM must be an existing directory.
TO must not exist, but its parent must exist.
Must be setuid root.
Ian Dall (ian@sibyl.eleceng.ua.oz.au)
and David MacKenzie (djm@ai.mit.edu) */
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <signal.h>
#include "system.h"
#ifdef STDC_HEADERS
#include <stdlib.h>
#include <errno.h>
#else
char *malloc ();
extern int errno;
#endif
#ifndef HIPRI
#define HIPRI -10
#endif
#ifdef DEBUG
#define link(FROM, TO) (printf("Linking %s to %s\n", FROM, TO), 0)
#define unlink(FILE) (printf("Unlinking %s\n", FILE), 0)
#endif
/* The name this program was run with. */
char *program_name;
char *basename ();
char *fullpath ();
char *parent_dir ();
char *xmalloc ();
void error ();
void strip_trailing_slashes ();
void
main (argc, argv)
int argc;
char **argv;
{
char *from, *from_parent, *from_base;
char *to, *to_parent, *to_parent_path;
struct stat from_stats, to_stats;
char *slash, temp;
int i;
program_name = argv[0];
if (argc != 3)
{
fprintf (stderr, "Usage: %s existing-dir new-dir\n", program_name);
exit (2);
}
from = argv[1];
to = argv[2];
strip_trailing_slashes (from);
strip_trailing_slashes (to);
from_parent = parent_dir (from);
to_parent = parent_dir (to);
/* Make sure `from' is not "." or "..". */
from_base = basename (from);
if (!strcmp (from_base, ".") || !strcmp (from_base, ".."))
error (1, 0, "cannot rename `.' or `..'");
/* Even with an effective uid of root, link fails if the target exists.
That is what we want, so don't unlink `to' first.
However, we do need to check that the directories that link and unlink
will modify exist and are writable by the user. */
if (stat (from, &from_stats))
error (1, errno, "%s", from);
if ((from_stats.st_mode & S_IFMT) != S_IFDIR)
error (1, 0, "`%s' is not a directory", from);
if (access (from_parent, W_OK))
error (1, errno, "cannot write to `%s'", from_parent);
if (access (to_parent, W_OK))
error (1, errno, "cannot write to `%s'", to_parent);
/* To prevent disconnecting the tree rooted at `from' from its parent,
quit if any of the directories in `to' are the same (dev and ino)
as the directory `from'. */
slash = to_parent_path = fullpath (to_parent);
while (*slash)
{
slash = index (slash, '/');
if (slash)
{
++slash;
temp = *slash;
*slash = '\0';
if (stat (to_parent_path, &to_stats))
error (1, errno, "%s", to_parent_path);
*slash = temp;
}
else
{
/* Last element of path. */
slash = "";
if (stat (to_parent_path, &to_stats))
error (1, errno, "%s", to_parent_path);
}
if (to_stats.st_dev == from_stats.st_dev
&& to_stats.st_ino == from_stats.st_ino)
error (1, 0, "`%s' is an ancestor of `%s'", from, to);
}
/* We can't make the renaming atomic, but we do our best. */
for (i = NSIG; i > 0; i--)
if (i != SIGKILL)
signal (i, SIG_IGN);
setuid (0); /* Make real uid 0 so it is harder to kill. */
nice (HIPRI - nice (0)); /* Raise priority. */
if (link (from, to))
error (1, errno, "cannot link `%s' to `%s'", from, to);
if (unlink (from))
error (1, errno, "cannot unlink `%s'", from);
/* Replace the directory's `..' entry. It used to be a link to
the parent of `from'; make it a link to the parent of `to' instead. */
i = strlen (to);
slash = xmalloc (i + 4);
strcpy (slash, to);
strcpy (slash + i, "/..");
if (unlink (slash) && errno != ENOENT)
error (1, errno, "cannot unlink `%s'", slash);
if (link (to_parent, slash))
error (1, errno, "cannot link `%s' to `%s'", to_parent, slash);
exit (0);
}
/* Return the name of the directory containing PATH. */
char *
parent_dir (path)
char *path;
{
char *dir;
char *base;
int length;
base = rindex (path, '/');
if (base == NULL)
return ".";
if (base > path)
base--;
length = base - path + 1;
dir = xmalloc (length + 1);
strncpy (dir, path, length);
dir[length] = '\0';
return dir;
}
/* Return NAME with any leading path stripped off. */
char *
basename (name)
char *name;
{
char *base;
base = rindex (name, '/');
return base ? base + 1 : name;
}
/* Return the full pathname (from /) of the directory DIR,
as static data. */
char *
fullpath (dir)
char *dir;
{
char wd[PATH_MAX + 2];
static char path[PATH_MAX + 2];
if (getwd (wd) == NULL)
error (1, errno, "cannot get current directory");
if (chdir (dir))
error (1, errno, "%s", dir);
if (getwd (path) == NULL)
error (1, errno, "cannot get current directory");
if (chdir (wd))
error (1, errno, "%s", wd);
return path;
}
/* Allocate N bytes of memory dynamically, with error checking. */
char *
xmalloc (n)
unsigned n;
{
char *p;
p = malloc (n);
if (p == 0)
error (1, 0, "virtual memory exhausted");
return p;
}
/* Remove any trailing slashes from STR. */
void
strip_trailing_slashes (str)
char *str;
{
int last = strlen (str) - 1;
while (last > 0 && str[last] == '/')
str[last--] = '\0';
}